package com.cnap.multiclouldmgmt.service.impl;


import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.cnap.multiclouldmgmt.model.RepByPage;
import com.cnap.multiclouldmgmt.model.alg.*;
import com.cnap.multiclouldmgmt.model.enums.CloudVendorsEnum;
import com.cnap.multiclouldmgmt.model.exception.JsonException;
import com.cnap.multiclouldmgmt.model.k8sModels.K8sClusterModel;
import com.cnap.multiclouldmgmt.model.k8sModels.NodeModel;
import com.cnap.multiclouldmgmt.model.scale.*;
import com.cnap.multiclouldmgmt.model.scale.db.ScalingConfigPO;
import com.cnap.multiclouldmgmt.model.scale.db.ScalingGroupPO;
import com.cnap.multiclouldmgmt.model.scale.db.ScalingPolicyPO;
import com.cnap.multiclouldmgmt.model.scale.db.ScalingResourcePO;
import com.cnap.multiclouldmgmt.model.scale.group.*;
import com.cnap.multiclouldmgmt.model.scale.intelligent.IndicatorEnum;
import com.cnap.multiclouldmgmt.model.scale.intelligent.QueryThresholdParam;
import com.cnap.multiclouldmgmt.model.scale.policy.*;
import com.cnap.multiclouldmgmt.repository.master.*;
import com.cnap.multiclouldmgmt.service.AlgorithmService;
import com.cnap.multiclouldmgmt.service.K8sClusterService;
import com.cnap.multiclouldmgmt.service.ScalingService;
import com.cnap.multiclouldmgmt.util.CnapCloudMgmtConstants;
import com.cnap.multiclouldmgmt.util.JsonUtil;
import com.cnap.multiclouldmgmt.util.PageUtils;
import com.cnap.multiclouldmgmt.util.TokenUtils;
import com.cnap.multiclouldmgmt.vm.web.VMScaleWeb;
import com.github.pagehelper.PageInfo;
import com.github.pagehelper.page.PageMethod;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * <类文件描述>
 *
 * @author xc
 **/
@Service
public class ScalingServiceImpl implements ScalingService {
    private static final Logger LOGGER = LoggerFactory.getLogger(ScalingServiceImpl.class);

    private static final String CNAP_MGMT_URL = "http://cnap-mgmt-be-svc.default.svc.cluster.local:8080";

    private final ScalingGroupMapper groupMapper;
    private final ScalingConfigMapper configMapper;
    private final ScalingPolicyMapper policyMapper;
    private final VMScaleWeb vmScaleWeb;
    private final ScalingResourceMapper resourceMapper;
    private final AlgorithmService algorithmService;
    private final K8sClusterInfoMapper k8sClusterInfoMapper;

    @Autowired
    public ScalingServiceImpl(ScalingGroupMapper groupMapper, ScalingConfigMapper configMapper,
                              ScalingPolicyMapper policyMapper, VMScaleWeb vmScaleWeb, ScalingResourceMapper resourceMapper,
                              AlgorithmService algorithmService, K8sClusterInfoMapper k8sClusterInfoMapper) {
        this.groupMapper = groupMapper;
        this.configMapper = configMapper;
        this.policyMapper = policyMapper;
        this.vmScaleWeb = vmScaleWeb;
        this.resourceMapper = resourceMapper;
        this.algorithmService = algorithmService;
        this.k8sClusterInfoMapper = k8sClusterInfoMapper;
    }

    /**
     * @param param 参数
     * @return
     */
    @Override
    public RepByPage<ScalingGroupVO> findByPage(QueryScalingGroupParam param) {
        PageMethod.startPage(param.getPageNum(), param.getPageSize());
        // 用户权限
        List<ScalingGroupPO> groupPOS = groupMapper.selectByParamWithUser(param, TokenUtils.getUserName());
        List<ScalingGroupVO> vos = new ArrayList<>();
        for (ScalingGroupPO po : groupPOS) {
            ScalingGroupVO vo = new ScalingGroupVO();
            vo.setId(po.getId());
            vo.setClusterName(po.getClusterName());
            vo.setProvider(po.getProvider());
            vo.setAccountName(po.getAccountName());
            vo.setDeletePolicy(po.getDeletePolicy());
            vo.setGroupName(po.getGroupName());
            vo.setExpectNum(po.getExpectNum());
            vo.setMaxNum(po.getMaxNum());
            vo.setMinNum(po.getMinNum());
            vo.setLabel(po.getLabel());
            // 获取当前实例数
            int currentNum = getCurrentNodeNum(po.getProvider(), po.getClusterName(), po.getLabel());
            vo.setCurrentNum(currentNum);
            vo.setStatus(po.getStatus());
            vo.setConfigName(po.getConfigName());

            List<ScalingPolicyPO> policyPOS = policyMapper.selectPolicyByGroupId(po.getId());
            List<ScalingPolicyVO> policyVOS = policyPOS.stream().map(policyPO -> {
                ScalingPolicyVO policyVO = new ScalingPolicyVO();
                policyVO.setPolicyId(policyPO.getId());
                policyVO.setType(policyPO.getType());
                policyVO.setContinuousNum(policyPO.getContinuousNum());
                policyVO.setDuration(policyPO.getDuration());
                policyVO.setCoolTime(policyPO.getCoolTime());
                policyVO.setNum(policyPO.getNum());
                policyVO.setResource(policyPO.getResource());
                policyVO.setValue(policyPO.getValue());
                return policyVO;
            }).collect(Collectors.toList());

            vo.setPolicyInfos(policyVOS);
            vos.add(vo);
        }
        PageInfo<ScalingGroupPO> pos = new PageInfo<>(groupPOS);
        PageInfo<ScalingGroupVO> voPageInfo = new PageInfo<>(vos);
        PageUtils.setRealPageInfo(voPageInfo, pos);
        return PageUtils.getPageResult(voPageInfo);
    }

    /**
     * 获取当前集群对应label的数量
     *
     * @param provider    云商
     * @param clusterName 集群名称
     * @param label       label ai/bigdata
     * @return 结果
     */
    private int getCurrentNodeNum(String provider, String clusterName, String label) {
        K8sClusterService k8sClusterService = SpringUtil.getBean(K8sClusterService.class);
        try {
            RepByPage<NodeModel> result = k8sClusterService.getK8sClusterNodeInfosByConditions(provider, clusterName,
                    null,
                    null, 1000, 1, CnapCloudMgmtConstants.NODE_APP_LABEL_ID, label);
            if ((result == null) || (result.getResult() == null)) {
                LOGGER.warn("getCurrentNodeNum provider={} clusterName={} label={} zero", provider, clusterName, label);
                return 0;
            }
            return result.getResult().size();
        } catch (Exception e) {
            LOGGER.error("getCurrentNodeNum exception ", e);
            throw new JsonException(500, "无法获取当前集群实例数");
        }
    }

    /**
     * @param scalingGroupDTO 伸缩组信息
     * @return
     */
    @Override
    public boolean createScalingGroup(ScalingGroupDTO scalingGroupDTO) {
        ScalingConfigPO configPO = configMapper.selectConfigById(scalingGroupDTO.getConfigId());
        if (Objects.isNull(configPO)) {
            LOGGER.error("there is no config in db, id={}", scalingGroupDTO.getConfigId());
            throw new JsonException(400, "未找到该伸缩配置");
        }
        if (!configPO.getUserName().equals(TokenUtils.getUserName())) {
            LOGGER.error("current user has no permissions, token user={}", TokenUtils.getUserName());
            throw new JsonException(401, "没有权限");
        }
        // 判断是否对同一个集群设置了多个策略分组
        // label为微服务id
        int labelCount = groupMapper.countByLabelWithUserAndClusterName(scalingGroupDTO.getLabel(),
                TokenUtils.getUserName(), scalingGroupDTO.getClusterName());
        if (labelCount > 0) {
            LOGGER.warn("current cluster has created scaling group with label={}", scalingGroupDTO.getLabel());
            throw new JsonException(500, "已在当前集群创建该云资源伸缩组");
        }

        ScalingGroupPO po = scalingGroupDTO.toPO();
        int insertResult = groupMapper.insertScalingGroup(po);
        return insertResult == 1;
    }

    /**
     * @param groupId 分组id
     * @return
     */
    @Override
    public boolean startScale(String groupId) {
        ScalingGroupPO po = groupMapper.selectGroupById(groupId);
        if (Objects.isNull(po)) {
            LOGGER.error("can't find group, id={}", groupId);
            throw new JsonException(404, "未找到该伸缩组信息");
        }
        if (ScalingGroupStatusEnum.START.getStatus() == po.getStatus()) {
            LOGGER.warn("this scaling group has started, id={}", groupId);
            throw new JsonException(400, "该伸缩组已启用");
        }
        // 没有问题，则更新状态
        int result = groupMapper.updateGroupStatusById(groupId, ScalingGroupStatusEnum.START.getStatus());
        // 通知执行引擎
        List<ScalingPolicyPO> policyPOS = policyMapper.selectPolicyByGroupId(groupId);
        LOGGER.info("get policy by groupId, {}", policyPOS);
        for (ScalingPolicyPO policyPO : policyPOS) {
            try {
                vmScaleWeb.addVmPolicy(policyPO.getId());
                LOGGER.info("notice vm scale to start success, groupId={}, policyId={}", groupId, policyPO.getId());
            } catch (InterruptedException e) {
                LOGGER.error("notice vm scale to start error: {}", e.getMessage());
                Thread.currentThread().interrupt();
                throw new JsonException(500, "内部错误");
            }
        }
        return result == 1;
    }

    /**
     * @param groupId 伸缩组id
     * @return
     */
    @Override
    public boolean stopScale(String groupId) {
        ScalingGroupPO po = groupMapper.selectGroupById(groupId);
        if (Objects.isNull(po)) {
            LOGGER.error("can't find group, id={}", groupId);
            throw new JsonException(404, "未找到该伸缩组信息");
        }
        if (ScalingGroupStatusEnum.STOP.getStatus() == po.getStatus()) {
            LOGGER.warn("this scaling group has stopped, id={}", groupId);
            return true;
        }
        // 通知执行引擎
        List<ScalingPolicyPO> policyPOS = policyMapper.selectPolicyByGroupId(groupId);
        LOGGER.info("get policy by groupId, {}", policyPOS);
        for (ScalingPolicyPO policyPO : policyPOS) {
            try {
                vmScaleWeb.deleteVmPolicy(policyPO.getId());
                LOGGER.info("notice vm scale to stop success, groupId={}, policyId={}", groupId, policyPO.getId());
            } catch (InterruptedException e) {
                LOGGER.error("notice vm scale to stop error: {}", e.getMessage());
                Thread.currentThread().interrupt();
                throw new JsonException(500, "内部错误");
            }
        }
        int result = groupMapper.updateGroupStatusById(groupId, ScalingGroupStatusEnum.STOP.getStatus());
        return result == 1;
    }

    /**
     * @param groupId                     伸缩组id
     * @param needSendDeleteRequestToCnap 是否需要同步更新cnap
     * @return
     */
    @Override
    @Transactional
    public boolean deleteScalingGroup(String groupId, boolean needSendDeleteRequestToCnap) {
        ScalingGroupPO po = groupMapper.selectGroupById(groupId);
        if (Objects.isNull(po)) {
            LOGGER.error("can't find group, id={}", groupId);
            throw new JsonException(404, "未找到该伸缩组信息");
        }
        if (ScalingGroupStatusEnum.START.getStatus() == po.getStatus()) {
            LOGGER.warn("this scaling group has started, id={}", groupId);
            throw new JsonException(400, "该伸缩组已启用，请先停用再删除");
        }
        // 1.删除策略
        List<ScalingPolicyPO> policyPOS = policyMapper.selectPolicyByGroupId(groupId);
        if (Objects.nonNull(policyPOS) && !policyPOS.isEmpty()) {
            int deletePolicyResult = policyMapper.deletePolicyByGroupId(groupId);
            if (deletePolicyResult == 0) {
                LOGGER.error("delete policy error, groupId={}", groupId);
                throw new JsonException(500, "内部错误");
            }
        }

        // 2.删除伸缩组
        int deleteGroupResult = groupMapper.deleteGroupById(groupId);
        if (deleteGroupResult != 1) {
            LOGGER.error("delete group error, groupId={}", groupId);
            throw new JsonException(500, "内部错误");
        }
        // 已在上述步骤判断，必须先停止在删除策略，故不用通知执行引擎

        // 通知应用服务删除策略
        if (needSendDeleteRequestToCnap) {
            CompletableFuture.runAsync(() -> {
                LOGGER.info("update cnap mgmt scaling by delete, clusterName={}, provider={}", po.getClusterName(), po.getProvider());
                updatePolicyToCnapMgmt(groupId, po.getClusterName(), po.getProvider(), po.getLabel());
            });
        }
        return true;
    }

    /**
     * @param scalingPolicyDTOs 伸缩策略信息
     * @return
     */
    @Override
    @Transactional
    public boolean createScalingPolicy(List<ScalingPolicyDTO> scalingPolicyDTOs) {
        boolean res = true;
        // 判断是否最多2个策略，且一个为扩容，一个缩容
        if (scalingPolicyDTOs.size() > 2) {
            LOGGER.error("can't create scaling policy more than 2");
            throw new JsonException(400, "无法创建多于2个扩缩容策略");
        }
        if (scalingPolicyDTOs.size() == 2) {
            List<String> types = scalingPolicyDTOs.stream().map(ScalingPolicyDTO::getType).collect(Collectors.toList());
            if (types.get(0).equals(types.get(1))) {
                LOGGER.error("there are 2 same policy type, type={}", types.get(0));
                throw new JsonException(400, "无法同时添加两个相同的扩缩容策略");
            }
        }
        // 判断是否存在伸缩组
        String groupId = scalingPolicyDTOs.get(0).getGroupId();
        ScalingGroupPO groupPO = groupMapper.selectGroupById(groupId);
        if (Objects.isNull(groupPO)) {
            LOGGER.error("can't get group info, groupId={}", groupId);
            throw new JsonException(404, "未找到伸缩组信息");
        }

        List<ScalingPolicyPO> scalingPolicyPOS = policyMapper.selectPolicyByGroupId(groupId);
        if (Objects.nonNull(scalingPolicyPOS) && !scalingPolicyPOS.isEmpty()) {
            // 有旧策略，则删除
            int deleteOldPolicyResult = policyMapper.deletePolicyByGroupId(groupId);
            if (deleteOldPolicyResult == 0) {
                LOGGER.error("delete policy error, groupId={}", groupId);
                throw new JsonException(500, "内部错误");
            }
        }
        // 插入新的策略
        for (ScalingPolicyDTO scalingPolicyDTO : scalingPolicyDTOs) {
            ScalingPolicyPO policyPO = scalingPolicyDTO.toPOWithoutId();
            policyPO.setId(IdUtil.simpleUUID());
            int result = policyMapper.insertScalingPolicy(policyPO);
            res = (result == 1) && res;
        }

        // 通知应用管理服务更新策略
        CompletableFuture.runAsync(() -> {
            LOGGER.info("update cnap mgmt scaling by insert new, clusterName={}, provider={}",
                    groupPO.getClusterName(), groupPO.getProvider());
            updatePolicyToCnapMgmt(groupId, groupPO.getClusterName(), groupPO.getProvider(), groupPO.getLabel());
        });
        return res;
    }

    private void updatePolicyToCnapMgmt(String groupId, String clusterName, String provider, String category) {
        List<ScalingPolicyPO> newScalingPolicyPOS = policyMapper.selectPolicyByGroupId(groupId);
        UpdateScalingParam param = new UpdateScalingParam();
        param.setScalingPolicyPOS(newScalingPolicyPOS);
        param.setClusterName(clusterName);
        param.setCategory(category);
        // tencentCloud 需要转换成Tencent
        String realProvider = CloudVendorsEnum.getCloudeTypeByDescribe(provider);
        param.setProvider(realProvider);
        String url = CNAP_MGMT_URL + "/sys/strategy/update";
        HttpResponse response = HttpRequest.post(url).body(JsonUtil.objectToJsonStr(param)).execute();
        if (!response.isOk()) {
            LOGGER.error("can't get right response from cnap mgmt, response={}", response);
        }
    }

    /**
     * @param param 参数
     * @return
     */
    @Override
    public RepByPage<ScalingConfigVO> findByPage(QueryScalingConfigParam param) {
        PageMethod.startPage(param.getPageNum(), param.getPageSize());
        List<ScalingConfigPO> configPOS = configMapper.selectByParam(param, TokenUtils.getUserName());

        List<ScalingConfigVO> vos = new ArrayList<>();
        for (ScalingConfigPO po : configPOS) {
            ScalingConfigVO vo = new ScalingConfigVO();
            vo.setConfigId(po.getId());
            vo.setCreateTime(po.getCreateTime());
            vo.setCloudResourceIds(po.getCloudResourceIds());
            vo.setConfigName(po.getConfigName());
            vo.setProvider(po.getProvider());
            vo.setAccountName(po.getAccountName());

            vos.add(vo);
        }
        PageInfo<ScalingConfigPO> pos = new PageInfo<>(configPOS);
        PageInfo<ScalingConfigVO> voPageInfo = new PageInfo<>(vos);
        PageUtils.setRealPageInfo(voPageInfo, pos);
        return PageUtils.getPageResult(voPageInfo);
    }

    /**
     * @param scalingConfigDTO 配置
     * @return
     */
    @Override
    public boolean createScalingConfig(ScalingConfigDTO scalingConfigDTO) {
        ScalingConfigPO configPO = scalingConfigDTO.toPO();
        int result = configMapper.insertScalingConfig(configPO);
        return result == 1;
    }

    /**
     * @param configId 配置id
     * @return
     */
    @Override
    public boolean deleteScalingConfigById(String configId) {
        ScalingConfigPO po = configMapper.selectConfigById(configId);
        if (Objects.isNull(po)) {
            LOGGER.error("can't find config, id={}", configId);
            throw new JsonException(404, "未找到该伸缩配置");
        }
        List<ScalingGroupPO> groupPOS = groupMapper.selectByConfigId(configId);
        if (Objects.nonNull(groupPOS) && !groupPOS.isEmpty()) {
            boolean res = groupPOS.stream().anyMatch(g -> g.getStatus() == ScalingGroupStatusEnum.START.getStatus());
            if (res) {
                LOGGER.error("current config has been used by scaling group, can't be deleted, configId={}", configId);
                throw new JsonException(400, "当前配置已被伸缩组使用");
            }
        }
        int result = configMapper.deleteConfigById(configId);
        return result == 1;
    }

    @Override
    public List<ScalingResourcePO> getAllCloudResources() {
        return resourceMapper.selectAll();
    }

    @Override
    public ScalingResourcePO getCloudResourceById(String resourceId) {
        return resourceMapper.selectResourceById(resourceId);
    }

    @Override
    @Transactional
    public void createDefaultScalingGroupForNewCluster(String userName, String clusterName, String cloudVendor, int currentNodeNum) {
        // 判断该集群是否已经有AI或者BIGDATA的伸缩组了
        List<ScalingGroupPO> existGroups = groupMapper.selectGroupByClusterName(clusterName);
        int size = existGroups.size();
        // 已经有伸缩组，直接返回
        if (size >= 1) {
            LOGGER.warn("current cluster({}) has added scaling group, just return",
                    clusterName);
            return;
        }
        // 如果没有伸缩组的化，需要手动创建2个，AI BIGDATA
        createDefault(userName, clusterName, cloudVendor, ScalingLabelEnum.AI, currentNodeNum);
        createDefault(userName, clusterName, cloudVendor, ScalingLabelEnum.BIGDATA, currentNodeNum);
        LOGGER.info("create default scaling group for cluster={} in cloud={} by user={}, current node={}", clusterName, cloudVendor, userName, currentNodeNum);
    }

    @Override
    public void createDefaultScalingGroupForNewCluster(String userName, String clusterName, String cloudVendor, String accountName,
                                                       int currentNodeNum, CreateScalingDTO createScalingDTO) {
        if (Objects.isNull(createScalingDTO)) {
            LOGGER.warn("CreateScalingDTO is null, try to stop and delete policy");
            // 为null，则删除本地策略
            List<ScalingGroupPO> scalingGroupPOS = groupMapper.selectGroupByClusterName(clusterName);
            if (scalingGroupPOS.size() >= 1) {
                delete(scalingGroupPOS, false);
                return;
            }
            return;
        }
        if (Objects.isNull(createScalingDTO.getScalingPolicyPOs()) || createScalingDTO.getScalingPolicyPOs().isEmpty()) {
            LOGGER.warn("policy is null, try to stop and delete, label={}", createScalingDTO.getScalingLabel());
            // 为null，则删除本地策略
            List<ScalingGroupPO> scalingGroupPOS = groupMapper.selectGroupByClusterNameAndLabel(clusterName,
                    createScalingDTO.getScalingLabel());
            if (scalingGroupPOS.size() >= 1) {
                delete(scalingGroupPOS, false);
                return;
            }
            return;
        }
        // 判断该集群是否已经有AI或者BIGDATA的伸缩组了
        List<ScalingGroupPO> existGroups = groupMapper.selectGroupByClusterNameAndLabel(clusterName,
                createScalingDTO.getScalingLabel());
        int size = existGroups.size();
        // 已经有伸缩组，直接返回
        if (size >= 1) {
            LOGGER.warn("current cluster({}) has added scaling group({}), delete and reInsert",
                    clusterName, createScalingDTO.getScalingLabel());
            delete(existGroups, false);
        }
        long timestamp = System.currentTimeMillis();
        // 1. config
        ScalingConfigPO scalingConfigPO = new ScalingConfigPO();
        // 使用集群名称-config-timestamp作为默认配置名称
        String defaultConfigName = clusterName + "-config-" + timestamp;
        scalingConfigPO.setConfigName(defaultConfigName);
        scalingConfigPO.setUserId(userName);
        scalingConfigPO.setUserName(userName);
        scalingConfigPO.setId(IdUtil.simpleUUID());
        scalingConfigPO.setCreateTime(LocalDateTime.now());
        scalingConfigPO.setCloudResourceIds(Collections.singletonList("1"));
        scalingConfigPO.setProvider(cloudVendor);
        scalingConfigPO.setAccountName(accountName);
        configMapper.insertScalingConfig(scalingConfigPO);
        LOGGER.info("create default config for {}, config={}", clusterName, scalingConfigPO);
        // 2. group
        ScalingGroupPO groupPO = new ScalingGroupPO();
        groupPO.setId(IdUtil.simpleUUID());
        groupPO.setStatus(ScalingGroupStatusEnum.INIT.getStatus());
        groupPO.setClusterName(clusterName);
        groupPO.setProvider(cloudVendor);
        // 使用默认标签
        groupPO.setLabel(createScalingDTO.getScalingLabel());
        // 默认使用先入后出
        groupPO.setDeletePolicy(ScalingDeletePolicyEnum.LIFO.getPolicy());
        String defaultGroupName = clusterName + "-group-" + timestamp;
        groupPO.setGroupName(defaultGroupName);
        groupPO.setExpectNum(currentNodeNum);
        groupPO.setMinNum(1);
        groupPO.setConfigId(scalingConfigPO.getId());
        groupPO.setAccountName(accountName);
        groupMapper.insertScalingGroup(groupPO);
        LOGGER.info("create default group for {}, group={}", clusterName, groupPO);

        for (ScalingPolicyPO scalingPolicyPO : createScalingDTO.getScalingPolicyPOs()) {
            scalingPolicyPO.setId(IdUtil.simpleUUID());
            scalingPolicyPO.setGroupId(groupPO.getId());
            policyMapper.insertScalingPolicy(scalingPolicyPO);
        }
        LOGGER.info("create scaling policy for {}, policys={}", clusterName, createScalingDTO.getScalingPolicyPOs());
        // 5. 启用伸缩组
        startScale(groupPO.getId());

    }

    private void createDefault(String userName, String clusterName, String cloudVendor, ScalingLabelEnum scalingLabelEnum, int currentNodeNum) {
        long timestamp = System.currentTimeMillis();
        // 1. config
        ScalingConfigPO scalingConfigPO = new ScalingConfigPO();
        // 使用集群名称-config-timestamp作为默认配置名称
        String defaultConfigName = clusterName + "-config-" + timestamp;
        scalingConfigPO.setConfigName(defaultConfigName);
        scalingConfigPO.setUserId(userName);
        scalingConfigPO.setUserName(userName);
        scalingConfigPO.setId(IdUtil.simpleUUID());
        scalingConfigPO.setCreateTime(LocalDateTime.now());
        scalingConfigPO.setCloudResourceIds(Collections.singletonList("1"));
        scalingConfigPO.setProvider(cloudVendor);
        configMapper.insertScalingConfig(scalingConfigPO);
        LOGGER.info("create default config for {}, config={}", clusterName, scalingConfigPO);
        // 2. group
        ScalingGroupPO groupPO = new ScalingGroupPO();
        groupPO.setId(IdUtil.simpleUUID());
        groupPO.setStatus(ScalingGroupStatusEnum.INIT.getStatus());
        groupPO.setClusterName(clusterName);
        groupPO.setProvider(cloudVendor);
        // 使用默认标签
        groupPO.setLabel(scalingLabelEnum.getLabel());
        // 默认使用先入后出
        groupPO.setDeletePolicy(ScalingDeletePolicyEnum.LIFO.getPolicy());
        String defaultGroupName = clusterName + "-group-" + timestamp;
        groupPO.setGroupName(defaultGroupName);
        groupPO.setExpectNum(currentNodeNum);
        groupPO.setMinNum(1);
        groupPO.setConfigId(scalingConfigPO.getId());
        groupMapper.insertScalingGroup(groupPO);
        LOGGER.info("create default group for {}, group={}", clusterName, groupPO);
        // 3. policy
        ScalingPolicyPO minPolicy = new ScalingPolicyPO();
        minPolicy.setId(IdUtil.simpleUUID());
        minPolicy.setGroupId(groupPO.getId());
        minPolicy.setType(ScalingPolicyTypeEnum.MIN.getType());
        minPolicy.setResource(ScalingPolicyResourceEnum.CPU.getResourceName());
        // 各种默认值
        minPolicy.setValue(30);
        minPolicy.setDuration(15);
        minPolicy.setCoolTime(900);
        minPolicy.setContinuousNum(3);
        minPolicy.setNum(1);

        ScalingPolicyPO maxPolicy = new ScalingPolicyPO();
        maxPolicy.setId(IdUtil.simpleUUID());
        maxPolicy.setGroupId(groupPO.getId());
        maxPolicy.setType(ScalingPolicyTypeEnum.MAX.getType());
        maxPolicy.setResource(ScalingPolicyResourceEnum.CPU.getResourceName());
        // 各种默认值
        maxPolicy.setValue(70);
        maxPolicy.setDuration(5);
        maxPolicy.setCoolTime(900);
        maxPolicy.setContinuousNum(3);
        maxPolicy.setNum(1);

        policyMapper.insertScalingPolicy(minPolicy);
        policyMapper.insertScalingPolicy(maxPolicy);
        LOGGER.info("create scaling policy for {}, policy={}, policy={}", clusterName, minPolicy, maxPolicy);

        // 5. 启用伸缩组
        startScale(groupPO.getId());
    }

    @Override
    @Transactional
    public void deleteByClusterName(String clusterName) {
        List<ScalingGroupPO> scalingGroupPOS = groupMapper.selectGroupByClusterName(clusterName);
        if (Objects.isNull(scalingGroupPOS) || scalingGroupPOS.isEmpty()) {
            LOGGER.info("this cluster={} has no scaling group, just return", clusterName);
            return;
        }
        delete(scalingGroupPOS, true);

    }

    private void delete(List<ScalingGroupPO> scalingGroupPOS, boolean needSendDeleteRequestToCnap) {
        for (ScalingGroupPO groupPO : scalingGroupPOS) {
            if (ScalingGroupStatusEnum.START.getStatus() == groupPO.getStatus()) {
                // 启动的需要停止
                stopScale(groupPO.getId());
            }
            // 停止后删除数据
            // 删除伸缩足
            deleteScalingGroup(groupPO.getId(), needSendDeleteRequestToCnap);
            // 删除配置
            deleteScalingConfigById(groupPO.getConfigId());
        }
    }

    @Transactional
    @Override
    public void updateScalingPolicy(List<CreateScalingDTO> dtos) {
        for (CreateScalingDTO dto : dtos) {
            List<ScalingGroupPO> existGroups = groupMapper.selectGroupByClusterNameAndLabel(dto.getClusterNames(),
                    dto.getScalingLabel());
            if (existGroups.size() < 1) {
                // 不存在的话，直接新建
                int currentNodeNum = getCurrentNodeNum(dto.getProvider(), dto.getClusterNames(), dto.getScalingLabel());
                K8sClusterModel model = k8sClusterInfoMapper.getK8sClusterInfoByName(dto.getClusterNames());
                String userName = model.getCreateUser();
                createDefaultScalingGroupForNewCluster(userName, dto.getClusterNames(), dto.getProvider(), dto.getAccountName(),
                        currentNodeNum, dto);
                break;
            }
            List<ScalingPolicyPO> scalingPolicyPOS = dto.getScalingPolicyPOs();
            if (Objects.isNull(scalingPolicyPOS)) {
                LOGGER.warn("scaling policy is null, try to stop and delete, cluster={}, label={}",
                        dto.getClusterNames(), dto.getScalingLabel());
                delete(existGroups, false);
                break;
            }

            // 更新
            for (ScalingGroupPO groupPO : existGroups) {
                // 类型不对直接返回
                if (!groupPO.getLabel().equals(dto.getScalingLabel())) {
                    continue;
                }
                // 先暂停
                stopScale(groupPO.getId());
                // 更新具体策略
                for (ScalingPolicyPO po : dto.getScalingPolicyPOs()) {
                    ScalingPolicyPO oldPo = policyMapper.selectPolicyByGroupIdAndType(groupPO.getId(), po.getType());
                    if (Objects.isNull(oldPo)) {
                        LOGGER.warn("can't find old policy by type={} and groupId={}", po.getType(), groupPO.getId());
                        continue;
                    }
                    oldPo.setValue(po.getValue());
                    oldPo.setNum(po.getNum());
                    oldPo.setResource(po.getResource());
                    oldPo.setCoolTime(po.getCoolTime());
                    oldPo.setDuration(po.getDuration());
                    oldPo.setContinuousNum(po.getContinuousNum());

                    policyMapper.updatePolicy(oldPo);
                }
                // 判断原始策略的启停状态，决定新策略的启停状态
                if (ScalingGroupStatusEnum.START.getStatus() == groupPO.getStatus()) {
                    startScale(groupPO.getId());
                }
            }
        }
    }

    @Override
    public String getIntelligentThreshold(QueryThresholdParam queryThresholdParam) {
        String resType = queryThresholdParam.getResType();

        if ("微服务".equals(resType)) {
            return getThresholdForSvc(queryThresholdParam);
        } else if ("节点".equals(resType)) {
            return getThresholdForNode(queryThresholdParam);
        } else {
            LOGGER.error("[intelligent-threshold]: unknown res type={}, return failed", resType);
            throw new JsonException(400, "未知类型");
        }
    }

    @Override
    public void deleteByClusterNameAndLabel(String clusterName, String label) {
        List<ScalingGroupPO> scalingGroupPOS = groupMapper.selectGroupByClusterNameAndLabel(clusterName, label);
        if (scalingGroupPOS.size() >= 1) {
            delete(scalingGroupPOS, false);
        }
    }

    private String getThresholdForNode(QueryThresholdParam queryThresholdParam) {
        List<String> nodeNames = queryThresholdParam.getNodeNames();
        if (IndicatorEnum.CPU.getName().equals(queryThresholdParam.getIndicatorName())) {
            // 查询微服务配置的扩缩容高阈值，小数表示
            double maxThresh = nodeNames.stream().map(nodeName -> {
                        RequestForCPU requestForCPU = new RequestForCPU();
                        requestForCPU.setNodeName(nodeName);
                        requestForCPU.setType("node");
                        requestForCPU.setClusterName(queryThresholdParam.getClusterName());
                        return algorithmService.getCPUThresh(requestForCPU);
                    }).filter(Objects::nonNull)
                    .mapToDouble(ResponseForCPU::getCpuThresh)
                    .max().orElse(0.6);// 默认0.6
            // 如果thresholds为空，则算法默认给0.6，即60%
            // 强转int
            return String.valueOf((int) NumberUtil.mul(maxThresh, 100));
        } else if (IndicatorEnum.RESPONSE_TIME.getName().equals(queryThresholdParam.getIndicatorName())) {
            // 目前就2种，若增加，需要使用else-if
            // 节点没有请求响应时间
            throw new JsonException(400, "节点没有请求响应时间指标");
        } else {
            // 显卡温度
            double maxThresh = nodeNames.stream().map(nodeName -> {
                        RequestForGPUTem requestForGPUTem = new RequestForGPUTem();
                        requestForGPUTem.setClusterName(queryThresholdParam.getClusterName());
                        requestForGPUTem.setNodeName(nodeName);
                        requestForGPUTem.setType("node");
                        return algorithmService.getGPUTemThresh(requestForGPUTem);
                    }).filter(Objects::nonNull)
                    .mapToDouble(ResponseForGPUTem::getGpuTemThresh)
                    .max().orElse(75);// 默认75
            return String.valueOf(maxThresh);
        }
    }

    private String getThresholdForSvc(QueryThresholdParam queryThresholdParam) {
        List<String> svcIds = queryThresholdParam.getSvcIds();
        if (IndicatorEnum.CPU.getName().equals(queryThresholdParam.getIndicatorName())) {
            // 查询微服务配置的扩缩容高阈值，小数表示
            List<Double> thresholds = svcIds.stream()
                    .map(svcId -> policyMapper.selectMaxValueByLabelAndResource(svcId, ScalingPolicyResourceEnum.CPU.getResourceName()))
                    .filter(Objects::nonNull)
                    .map(po -> NumberUtil.div(po.getValue(), 100.00))
                    .collect(Collectors.toList());
            RequestForCPU requestForCPU = new RequestForCPU();
            requestForCPU.setUserThresholdList(thresholds);
            ResponseForCPU responseForCPU = algorithmService.getCPUThresh(requestForCPU);
            // 如果thresholds为空，则算法默认给0.6，即60%
            // 强转int
            return String.valueOf((int) NumberUtil.mul(responseForCPU.getCpuThresh(), 100));
        } else if (IndicatorEnum.RESPONSE_TIME.getName().equals(queryThresholdParam.getIndicatorName())) {
            // 目前就2种，若增加，需要使用else-if
            double maxThresh = svcIds.stream().map(svcId -> {
                        RequestForResTime requestForResTime = new RequestForResTime();
                        requestForResTime.setSvcId(svcId);
                        return algorithmService.getResTimeThresh(requestForResTime);
                    }).filter(Objects::nonNull)
                    .mapToDouble(ResponseForResTime::getResponseTimeThresh)
                    .max().orElse(1500.00);// 算法处默认给1500
            return String.valueOf(maxThresh);
        } else {
            // 显卡温度
            double maxThresh = svcIds.stream().map(svcId -> {
                        RequestForGPUTem requestForGPUTem = new RequestForGPUTem();
                        requestForGPUTem.setSvcId(svcId);
                        requestForGPUTem.setType("service");
                        return algorithmService.getGPUTemThresh(requestForGPUTem);
                    }).filter(Objects::nonNull)
                    .mapToDouble(ResponseForGPUTem::getGpuTemThresh)
                    .max().orElse(75);// 默认75
            return String.valueOf(maxThresh);
        }
    }
}
